package org.alexsotob.roo.addon.flyway;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.Plugin;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Element;

@Component
@Service
public class FlywayMavenOperationsImpl implements FlywayMavenOperations {

	private static final String PASSWORD_TAG_NAME = "password";

	private static final String USER_TAG_NAME = "user";

	private static final String URL_TAG_NAME = "url";

	private static final String DRIVER_TAG_NAME = "driver";

	private static final String FLYWAY_MAVEN_PLUGIN = "flyway-maven-plugin";

	private static final char SEPARATOR = File.separatorChar;

	static final String DATABASE_PROPERTIES = "database.properties";
	static final String APPLICATION_CONTEXT_XML = "applicationContext.xml";

	@Reference
	ProjectOperations projectOperations;

	@Reference
	FileManager fileManager;

	@Override
	public boolean isFlywayMavenInstallationPossible() {
		return projectOperations.isProjectAvailable()
				&& fileManager.exists(projectOperations
						.getProjectMetadata()
						.getPathResolver()
						.getIdentifier(
								Path.SRC_MAIN_RESOURCES,
								"META-INF" + SEPARATOR + "spring" + SEPARATOR
										+ DATABASE_PROPERTIES));
	}

	@Override
	public boolean isFlywayMavenUnistallationPossible() {
		return isBuildPluginRegistered();
	}

	private boolean isBuildPluginRegistered() {

		List<Element> flywayBuildPlugins = getPluginsDefinitionFromConfiguration();

		for (Element plugin : flywayBuildPlugins) {
			// With Spring Roo 1.2.0 project operations have an
			// isInstalledFeature,
			// but for backwards compatibility dependency attribute is used.
			boolean pluginRegistered = this.projectOperations
					.getProjectMetadata().isBuildPluginRegistered(
							new Plugin(plugin));
			if (!pluginRegistered) {
				return false;
			}
		}

		return true;

	}

	@Override
	public void removeFlywayMavenPlugin() {
		removeFlywayBuildPlugin();
	}

	private void removeFlywayBuildPlugin() {
		List<Element> flywayBuildPlugins = getPluginsDefinitionFromConfiguration();
		List<Plugin> plugins = new ArrayList<Plugin>();

		for (Element pluginElement : flywayBuildPlugins) {

			if (isFlywayMavenPlugin(pluginElement)) {
				plugins.add(new Plugin(pluginElement));
			}
		}
		projectOperations.removeBuildPlugins(plugins);
	}

	@Override
	public void configureFlywayMavenPlugin() {
		addFlywayBuildPlugins();
	}

	private void addFlywayBuildPlugins() {
		List<Element> flywayBuildPlugins = getPluginsDefinitionFromConfiguration();

		for (Element pluginElement : flywayBuildPlugins) {

			if (isFlywayMavenPlugin(pluginElement)) {
				updateConfigurationTagWithDatabaseParameters(pluginElement);
			}
			projectOperations.addBuildPlugin(new Plugin(pluginElement));
		}
	}

	private List<Element> getPluginsDefinitionFromConfiguration() {
		Element configuration = XmlUtils.getConfiguration(getClass());

		List<Element> flywayBuildPlugins = XmlUtils.findElements(
				"/configuration/flyway/plugins/plugin", configuration);
		return flywayBuildPlugins;
	}

	private void updateConfigurationTagWithDatabaseParameters(
			Element pluginElement) {

		InputStream propertiesInputStream = this.fileManager
				.getInputStream(projectOperations.getPathResolver()
						.getIdentifier(
								Path.SRC_MAIN_RESOURCES,
								"META-INF" + SEPARATOR + "spring" + SEPARATOR
										+ DATABASE_PROPERTIES));

		try {
			DataSourcePropertyExtractor dataSourcePropertyExtractor = DataSourcePropertyExtractor
					.getInstance(propertiesInputStream);
			updateTagElement(pluginElement, DRIVER_TAG_NAME,
					dataSourcePropertyExtractor.getDriverClassName());
			updateTagElement(pluginElement, URL_TAG_NAME,
					dataSourcePropertyExtractor.getUrlConnection());
			updateTagElement(pluginElement, USER_TAG_NAME,
					dataSourcePropertyExtractor.getUsername());
			updateTagElement(pluginElement, PASSWORD_TAG_NAME,
					dataSourcePropertyExtractor.getPassword());
		} catch (IOException e) {
			throw new IllegalArgumentException(e);
		}
	}

	private void updateTagElement(Element pluginElement, String field,
			String value) {
		Element element = XmlUtils.findFirstElementByName(field, pluginElement);
		element.setTextContent(value);
	}

	private boolean isFlywayMavenPlugin(Element pluginElement) {
		return FLYWAY_MAVEN_PLUGIN.equals(pluginElement
				.getElementsByTagName("artifactId").item(0).getTextContent());
	}

}
